﻿namespace Trading.Studio

open System
open System.Collections.ObjectModel
open System.Reactive.Disposables
open System.Reactive.Linq
open System.Windows.Controls
open System.Collections.Generic
open System.Data.SqlClient

open com.ib.client

open Trading
open Trading.IBApi
open Trading.Observable
open Trading.IBApi.ContractExtensions

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MainWindow = 

    type Events = 
        | TrackPrice
        | EnterPosition

    type View() as this = 
        inherit ViewBase<MainWindowViewModel, Events>(windowResource = "MainWindow")

        let trackPriceControl : Button = this?TrackPriceControl
        let enterPositionControl : Button = this?EnterPositionControl
        let underlyingControl : TextBox = this?UnderlyingControl
        let strikeControl : TextBox = this?StrikeControl
        let stoplossLimit : TextBox = this?StopLossLimit
        let profittakingLimit : TextBox = this?ProfitTakingLimit

        override this.EventStreams = 
            [|
                trackPriceControl.Click >> Observable.Return TrackPrice 
                enterPositionControl.Click >> Observable.Return EnterPosition 
            |]

    type Controller(model, brokerGateway : IBrokerGateway) =
        inherit Controller<MainWindowViewModel, Events>(model)

        //---------- Private

        let callPriceResource = new MutableDisposable()
        let putPriceResource = new MutableDisposable()

        do 
            model.Expiration <- DateTime.Now
            model.Legs <- ObservableCollection<LegViewModel>()
            brokerGateway.Errors.ObserveOnDispatcher().Add(fun x -> model.Error <- x)
            model.Title <- brokerGateway.ConnectionInfo

        let updateAmountToEnter() = 
            model.AmountToEnter <-  model.Legs |> Seq.sumBy (fun x -> x.Price * x.Multiplier)

        let trackLegPrice contract legIndex = 
            brokerGateway.Ticks contract 
            |> Price.ask
            |> DispatcherObservable.ObserveOnDispatcher
            |> Observable.subscribe 
                (fun ask -> 
                    let legs = model.Legs
                    legs.[legIndex].Price <- ask 
                    updateAmountToEnter()
                )

        let insertPosition connection transaction = 
            let cmd = 
                new SqlCommand(
                    "INSERT INTO Position (StoplossLimit, ProfittakingLimit) VALUES(@StoplossLimit, @ProfittakingLimit);
                    SELECT SCOPE_IDENTITY()",
                    connection, 
                    transaction)
            let sqlParams = 
                [|
                    SqlParameter("StoplossLimit", model.StopLossLimit)
                    SqlParameter("ProfittakingLimit", model.ProfitTakingLimit)
                |]
            cmd.Parameters.AddRange(sqlParams);
            cmd.ExecuteScalar() |> Convert.ToInt32

        let insertLegs connection transaction positionId = 
            let insertLeg (underlying : string) (l : LegViewModel) = 
                let cmd = 
                    new SqlCommand(
                        "INSERT INTO Leg (Underlying, Multiplier, Side, Price, PositionId) 
                        VALUES (@Underlying, @Multiplier, @Side, @Price, @PositionId)",
                        connection, 
                        transaction)
                let sqlParams = 
                    [|
                        SqlParameter("Underlying", value = underlying)
                        SqlParameter("Multiplier", value = l.Multiplier)
                        SqlParameter("Side", value = string l.Side)
                        SqlParameter("Price", value = l.Price)
                        SqlParameter("PositionId", value = positionId)
                    |]
                cmd.Parameters.AddRange(sqlParams)
                let recordsAffected = cmd.ExecuteNonQuery()
                assert (recordsAffected = 1)

            model.Legs |> Seq.iter (insertLeg model.Underlying)
                    

        static let mutable factory = fun() ->
            let ib = new JSharpClient(host = "10.90.133.40", port = 4001, clientId = 10)
            Controller(model = ViewModel.Create(), brokerGateway = ib)

        //---------- Ctor/Factory

        static member Create with get() = factory and set(value) = factory <- value

        //---------- Interface

        override this.HandleEvent(model, event) = 
            match event with 
            | TrackPrice -> this.TrackPrice()
            | EnterPosition -> this.EnterPosition()

        member this.TrackPrice() = 
            let optionParams = model.Underlying, model.Expiration, model.Strike
            let call = Contract.Call optionParams
            let put = Contract.Put optionParams
            let legs = model.Legs
            legs.Clear()
            legs.Add(item = LegViewModel.Create(ActionSide.Buy, Call))
            legs.Add(item = LegViewModel.Create(ActionSide.Buy, Put))

            callPriceResource.Disposable <- trackLegPrice call 0
            putPriceResource.Disposable <- trackLegPrice put 1

        member this.EnterPosition() = 
            let comboContract = Contract(m_symbol = model.Underlying, m_exchange = "SMART", m_secType = "BAG", m_currency = "USD")
            let optionParams = model.Underlying, model.Expiration, model.Strike
            let call = Contract.Call optionParams
            let put = Contract.Put optionParams
            let callLeg = brokerGateway.CreateComboLeg(call, ActionSide.Buy) |> Async.RunSynchronously 
            let putLeg = brokerGateway.CreateComboLeg(put, ActionSide.Buy) |> Async.RunSynchronously 
            comboContract.m_comboLegs.add(callLeg) |> ignore
            comboContract.m_comboLegs.add(putLeg) |> ignore

            let order = Order(m_action =  ActionSide.Buy.ToString(), m_totalQuantity = 1, m_orderType = "Market")
            brokerGateway.PlaceOrder(comboContract, order) |> Async.RunSynchronously 

            use connection = new SqlConnection("Data Source=.\SQLExpress;Initial Catalog=Trading;Integrated Security=True")
            connection.Open()
            use transaction = connection.BeginTransaction()
            let positionId = insertPosition connection transaction
            insertLegs connection transaction positionId
            transaction.Commit()

